﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace DotNETX.Utility
{
    public static class EncryptDecryptUtils
    {
        private static bool IsNullOrBlank(string text)
        {
            if (!string.IsNullOrEmpty(text))
            {
                return string.IsNullOrEmpty(text.Trim());
            }
            return true;
        }

        private static string Bytes2HexString(byte[] bytes)
        {
            //StringBuilder sb = new StringBuilder();

            //for (int i = 0; i < bytes.Length; i++)
            //{
            //    sb.Append(string.Format("{0:X}", bytes[i]));
            //}
            //return sb.ToString();

            return BitConverter.ToString(bytes).Replace("-", string.Empty);
        }

        private static byte[] HexString2Bytes(string hex)
        {
            hex = hex.Replace("-", "");
            // 需要将 hex 转换成 byte 数组。 
            byte[] bytes = new byte[hex.Length / 2];

            for (int i = 0; i < bytes.Length; i++)
            {
                try
                {
                    // 每两个字符是一个 byte。 
                    bytes[i] = byte.Parse(hex.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
                }
                catch
                {
                    // Rethrow an exception with custom message. 
                    throw new ArgumentException("hex is not a valid hex number!", "hex");
                }
            }
            return bytes;
        }


        #region DES （key和iv都是8字节 64位）

        /// <summary>
        /// 
        /// </summary>
        /// <param name="plainText"></param>
        /// <param name="key"></param>
        /// <returns>HexString</returns>
        public static string DESEncrypt(string plainText, string key)
        {
            return DESEncrypt(plainText, key, Encoding.Default);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="plainText"></param>
        /// <param name="key"></param>
        /// <param name="enc"></param>
        /// <returns>HexString</returns>
        public static string DESEncrypt(string plainText, string key, Encoding enc)
        {
            enc = (enc == null ? Encoding.Default : enc);
            return DESEncrypt(plainText, key, (str) => { return enc.GetBytes(str); }, enc);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="plainText"></param>
        /// <param name="key"></param>
        /// <param name="KeyIV2Bs"></param>
        /// <returns>HexString</returns>
        public static string DESEncrypt(string plainText, string key, Func<string, byte[]> KeyIV2Bs, Encoding enc)
        {
            enc = (enc == null ? Encoding.Default : enc);
            return DESEncrypt(plainText, key, key, KeyIV2Bs,
                (byts) =>
                {
                    return Bytes2HexString(byts);
                }, (str) =>
                {
                    return enc.GetBytes(str);
                });
        }

        public static string DESEncrypt(string plainText, string key, string iv, Func<string, byte[]> KeyIV2Bs, Func<byte[], string> Bs2S, Func<string, byte[]> S2Bs)
        {
            byte[] bytesDESKey = KeyIV2Bs(key);
            byte[] bytesDESIV = null;
            if (!IsNullOrBlank(iv))
            {
                bytesDESIV = KeyIV2Bs(iv);
                return DESEncrypt(plainText, bytesDESKey, bytesDESIV, Bs2S, S2Bs);
            }
            else
            {
                return DESEncrypt(plainText, bytesDESKey, bytesDESKey, Bs2S, S2Bs);
            }
        }

        public static string DESEncrypt(string plainText, byte[] key, byte[] iv, Func<byte[], string> Bs2S, Func<string, byte[]> S2Bs)
        {
            if (IsNullOrBlank(plainText))
            {
                throw new ArgumentNullException("加密字串为空");
            }
            return iv == null || iv.Length == 0 ? Bs2S(DESEncryptData(S2Bs(plainText), key)) : Bs2S(DESEncryptData(S2Bs(plainText), key, iv));
        }

        public static byte[] DESEncryptData(byte[] dataToEncrypt, byte[] key)
        {
            return DESEncryptData(dataToEncrypt, key, key);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataToEncrypt"></param>
        /// <param name="key">8字节</param>
        /// <param name="iv">8字节</param>
        /// <returns></returns>
        public static byte[] DESEncryptData(byte[] dataToEncrypt, byte[] key, byte[] iv)
        {
            DESCryptoServiceProvider des = new DESCryptoServiceProvider
            {
                Key = key,
                IV = iv == null ? key : iv
            };
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(dataToEncrypt, 0, dataToEncrypt.Length);
                    cs.FlushFinalBlock();
                }
                return ms.ToArray();
            }
        }


        #region Decrypt

        /// <summary>
        /// 
        /// </summary>
        /// <param name="encryptedText">HexString</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string DESDecrypt(string encryptedText, string key)
        {
            return DESDecrypt(encryptedText, key, Encoding.Default);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="encryptedText">HexString</param>
        /// <param name="key"></param>
        /// <param name="enc"></param>
        /// <returns></returns>
        public static string DESDecrypt(string encryptedText, string key, Encoding enc)
        {
            enc = (enc == null ? Encoding.Default : enc);
            return DESDecrypt(encryptedText, key, (str) => { return enc.GetBytes(str); }, enc);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="encryptedText">HexString</param>
        /// <param name="key"></param>
        /// <param name="KeyIV2Bs"></param>
        /// <param name="enc"></param>
        /// <returns></returns>
        public static string DESDecrypt(string encryptedText, string key, Func<string, byte[]> KeyIV2Bs, Encoding enc)
        {
            enc = (enc == null ? Encoding.Default : enc);
            return DESDecrypt(encryptedText, key, key, KeyIV2Bs, (str) => { return HexString2Bytes(str); }, (byts) => { return enc.GetString(byts); });
        }

        public static string DESDecrypt(string encryptedText, string key, string iv, Func<string, byte[]> KeyIV2Bs, Func<string, byte[]> S2Bs, Func<byte[], string> Bs2S)
        {
            byte[] bytesDESKey = KeyIV2Bs(key);
            byte[] bytesDESIV = null;
            if (!IsNullOrBlank(iv))
            {
                bytesDESIV = KeyIV2Bs(iv);
                return DESDecrypt(encryptedText, bytesDESKey, bytesDESIV, S2Bs, Bs2S);
            }
            else
            {
                return DESDecrypt(encryptedText, bytesDESKey, bytesDESKey, S2Bs, Bs2S);
            }
        }

        public static string DESDecrypt(string encryptedText, byte[] key, byte[] iv, Func<string, byte[]> S2Bs, Func<byte[], string> Bs2S)
        {
            byte[] dataToDecrypt = S2Bs(encryptedText);
            return Bs2S(DESDecryptData(dataToDecrypt, key, iv));
        }

        public static byte[] DESDecryptData(byte[] dataToDecrypt, byte[] key)
        {
            return DESDecryptData(dataToDecrypt, key, key);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataToDecrypt"></param>
        /// <param name="key">8字节</param>
        /// <param name="iv">8字节</param>
        /// <returns></returns>
        public static byte[] DESDecryptData(byte[] dataToDecrypt, byte[] key, byte[] iv)
        {
            DESCryptoServiceProvider des = new DESCryptoServiceProvider
            {
                Key = key,
                IV = iv == null ? key : iv
            };
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(dataToDecrypt, 0, dataToDecrypt.Length);
                    cs.FlushFinalBlock();
                }
                return ms.ToArray();
            }
        }

        #endregion

        #endregion

        #region Md5

        public static string MD5Encrption(string plainText)
        {
            return MD5Encrption(plainText, (str) => { return Encoding.Default.GetBytes(str); });
        }

        public static string MD5Encrption(string plainText, Func<string, byte[]> S2Bs)
        {
            if (IsNullOrBlank(plainText))
            {
                throw new ArgumentNullException("加密字串为空");
            }
            byte[] original = S2Bs(plainText);
            return BitConverter.ToString(MD5EncrptionBytes(original, 0, original.Length)).ToUpperInvariant().Replace("-", string.Empty);
        }


        public static string MD5Encrption(string plainText, string salted, Func<string, byte[]> S2Bs, Func<byte[], string> Bs2S)
        {
            return Bs2S(MD5EncrptionBytes(plainText, salted, S2Bs));
        }

        public static byte[] MD5EncrptionBytes(string plainText, string salted, Func<string, byte[]> S2Bs)
        {
            if (IsNullOrBlank(plainText))
            {
                throw new ArgumentNullException("加密字串为空");
            }
            if (IsNullOrBlank(salted))
            {
                throw new ArgumentNullException("Salted字串为空");
            }
            byte[] original = S2Bs(plainText.Trim());
            byte[] saltValue = S2Bs(salted.Trim());
            byte[] toMd5 = new byte[original.Length + saltValue.Length];
            original.CopyTo(toMd5, 0);
            saltValue.CopyTo(toMd5, original.Length);
            return MD5EncrptionBytes(toMd5, 0, toMd5.Length);
        }

        public static byte[] MD5EncrptionBytes(byte[] toMd5, int offset, int count)
        {
            MD5 md5 = MD5.Create();
            byte[] saltPwd = md5.ComputeHash(toMd5, offset, count);
            Buffer.BlockCopy(md5.ComputeHash(saltPwd, 8, 8), 8, saltPwd, 8, 8);
            return saltPwd;
        }

        public static byte[] MD5Hash(string plainText)
        {
            return MD5Hash(Encoding.Default.GetBytes(plainText));
        }

        public static byte[] MD5Hash(string plainText, Encoding enc)
        {
            enc = (enc == null ? Encoding.Default : enc);
            return MD5Hash(enc.GetBytes(plainText));
        }

        public static byte[] MD5Hash(string plainText, Func<string, byte[]> S2Bs)
        {
            byte[] toMd5 = S2Bs(plainText);
            return MD5.Create().ComputeHash(toMd5, 0, toMd5.Length);
        }

        public static byte[] MD5Hash(byte[] toMd5)
        {
            return MD5.Create().ComputeHash(toMd5);
        }

        public static byte[] MD5Hash(byte[] toMd5, int offset, int count)
        {
            return MD5.Create().ComputeHash(toMd5, offset, count);
        }

        #endregion

    }
}
